package com.xc.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ObjectMapper om;

    // private static ObjectMapper om;

    /**
     * 在该类注册到Bean后执行的初始化操作。
     * 这里处理静态方法依赖注入的问题
     * 静态方法类级别，依赖注入对象级别，静态方法不能用依赖注入的对象
     */
    // @PostConstruct
    // private void init(){
    //     RedisUtil.om = this.om;
    // }

    // keys

    /**
     * 返回指定格式的key
     *
     * @param keyPattern key的格式，*返回所有，其余类似like
     * @return 符合格式的key集合
     */
    public Set<String> keys(String keyPattern) {
        try {
            return redisTemplate.keys(keyPattern);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 指定key失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     * @return 是否设置成功
     */
    public Boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 删除keys
     *
     * @param keys 可以传一个值 或多个
     */
    public void del(String... keys) {
        if (keys != null && keys.length > 0) {
            if (keys.length == 1) {
                redisTemplate.delete(keys[0]);
            } else {
                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(keys));
            }
        }
    }

    // String

    /**
     * 获取指定key的值,不存在返回null
     *
     * @param key 键
     * @return 对应的值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通放入，不存在则新建，存在则更新
     *
     * @param key   键
     * @param value 值
     * @return 是否成功
     */
    public Boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通放入并设置过期时间
     *
     * @param key   键
     * @param value 值
     * @param time  有效时长，以秒为单位，time要大于0 如果time小于等于0 将设置无限期
     * @return 是否成功
     */
    public Boolean set(String key, Object value, Long time) {
        try {
            redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 放入，不存在则新建，存在则没操作
     *
     * @param key   键
     * @param value 值
     * @return 是否成功
     */
    public Boolean setnx(String key, Object value) {
        try {
            redisTemplate.opsForValue().setIfAbsent(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 放入并设置过期时间，不存在则新建，存在则没操作
     *
     * @param key   键
     * @param value 值
     * @param time  有效时长，以秒为单位，time要大于0 如果time小于等于0 将设置无限期
     * @return 是否成功
     */
    public Boolean setnx(String key, Object value, Long time) {
        try {
            redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     *
     * @param key 键
     * @param num 要增加几（num > 0）
     * @return 增加后的结果
     */
    public Long incr(String key, Long num) {
        if (num < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        try {
            return redisTemplate.opsForValue().increment(key, num);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 递减
     *
     * @param key 键
     * @param num 要减少几（num > 0）
     * @return 增加后的结果
     */
    public Long decr(String key, Long num) {
        if (num < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        try {
            return redisTemplate.opsForValue().increment(key, -num);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    // Hash

    /**
     * 获取指定key的所有键值对
     *
     * @param key 键
     * @return 该key的键值对
     */
    public Map<Object, Object> hKeys(String key) {
        try {
            return redisTemplate.opsForHash().entries(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取key对应的hash表中的指定键的值,不存在返回null
     *
     * @param key  键 不能为null
     * @param item hash键 不能为null
     * @return 值
     */
    public Object hGet(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建，表也是
     *
     * @param key     键
     * @param hashKey hash键
     * @param value   值
     * @return true 成功 false失败
     */
    public Boolean hSet(String key, String hashKey, Object value) {
        try {
            redisTemplate.opsForHash().put(key, hashKey, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据并设置过期时间,如果不存在将创建，表也是
     *
     * @param key     键
     * @param hashKey hash键
     * @param value   值
     * @param time    时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public Boolean hSet(String key, String hashKey, Object value, Long time) {
        try {
            redisTemplate.opsForHash().put(key, hashKey, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的某个键值对，不存在则忽略
     *
     * @param key      键 不能为null
     * @param hashKeys hash键 可以多个 不能为null
     */
    public void hDel(String key, Object... hashKeys) {
        redisTemplate.opsForHash().delete(key, hashKeys);
    }

    /**
     * 判断hash表中是否有该键的值
     *
     * @param key     键 不能为null
     * @param hashKey hash键 不能为null
     * @return true 存在 false不存在
     */
    public Boolean hExists(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key     键
     * @param hashKey hash的键
     * @param num     要增加几(大于0)
     * @return 递增后的值
     */
    public Long hIncrBy(String key, String hashKey, Long num) {
        return redisTemplate.opsForHash().increment(key, hashKey, num);
    }

    /**
     * hash递减
     *
     * @param key     键
     * @param hashKey hash的键
     * @param num     要减少记(小于0)
     * @return 递减后的值
     */
    public Long hDecrBy(String key, String hashKey, Long num) {
        return redisTemplate.opsForHash().increment(key, hashKey, -num);
    }

    // Set

    /**
     * 根据key获取Set中的所有值。
     *
     * @param key 键
     * @return
     */
    public Set<Object> sMembers(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将数据放入set缓存,当key 不是集合类型时，返回一个错误。
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public Long sAdd(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 将set数据放入缓存,并设置过期时间
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public Long sAdd(String key, Long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0) {
                expire(key, time);
            }
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 获取set的长度
     *
     * @param key 键
     * @return 长度
     */
    public Long sCard(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 移除一个或多个value
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public Long sRem(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().remove(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    // List

    /**
     * 获取list指定范围内的元素
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     * @return 范围内元素
     */
    public List<Object> lRange(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list的长度
     *
     * @param key 键
     * @return 长度
     */
    public Long lLen(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 通过索引 获取list中指定索引的值
     *
     * @param key   键
     * @param index 索引 index>=0时，0：第一个元素，1：第二个元素，依次类推；index<0时，-1：最后一个元素，-2：倒数第二个元素，依次类推。不在范围返回null
     * @return
     */
    public Object lIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 在list最后插入一个值
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public Boolean rPush(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 在list头部插入一个值
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public Boolean lPush(String key, Object value) {
        try {
            redisTemplate.opsForList().leftPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除最后一个元素
     *
     * @param key 键
     * @return 被移除的元素
     */
    public Object rPop(String key) {
        try {
            return redisTemplate.opsForList().rightPop(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 移除第一个元素
     *
     * @param key 键
     * @return 被移除的元素
     */
    public Object lPop(String key) {
        try {
            return redisTemplate.opsForList().leftPop(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 尾插设置过期时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public Boolean rPush(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 尾插设置过期时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public Boolean lPush(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().leftPush(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 多值放入尾部
     *
     * @param key   键集合
     * @param value 值
     * @return
     */
    public Boolean rPushx(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 多值放入尾部，并设置过期时间
     *
     * @param key   键
     * @param value 值集合
     * @param time  时间(秒)
     * @return
     */
    public Boolean rPushx(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引设置修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    public Boolean lSet(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个。count > 0 : 从表头开始向表尾搜索。count < 0 : 从表尾开始向表头搜索，数量为 COUNT 的绝对值。count = 0 : 移除表中所有与 VALUE 相等的值。
     * @param value 值
     * @return 移除的个数
     */
    public Long lRem(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    /**
     * 转换方法，将get到的Object对象转换为具体对象
     *
     * @param o   redisUtil的get方法得到的结果
     * @param cla 要转的具体的对象类型
     * @return 具体的对象类型
     */
    public <M, T> T objConvertEntity(M o, Class<T> cla) {
        String strObj;
        try {
            strObj = o instanceof String ? (String) o : om.writeValueAsString(o);
            return om.readValue(strObj, cla);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public <M, T> List<T> objConvertList(M o, Class<T> cla) {
        String strObj;
        try {
            strObj = om.writeValueAsString(o);
            // return om.readValue(strObj, new TypeReference<List<T>>() {});
            return om.readValue(strObj, om.getTypeFactory().constructCollectionType(List.class, cla));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}
